package com.bitsfromspace.ubertorrent;

import com.bitsfromspace.ubertorrent.peer.PeerAddress;

/**
 * Represents a block of a {@link Piece}. It hold a {@link #getStatus()} and a {@link #getPeer()}.
 * <br/>
 * <table border=1>
 * 	<tr><th>Status:</th><th>Peer:</th><th>Meaning:</th></tr>
 * <tr><td>{@link Status#MISSING}</td><td><b>NULL</b></td><td>This block is missing (it isn't requested either). {@link #getPeer()} will return NULL</td></tr>
 * <tr><td>{@link Status#REQUESTED}</td><td>Peer of which this Block was requested. </td><td>This block is still missing, but is requested from {@link #getPeer()}. If the peer chockes us, the status will fall back to {@link Status#MISSING} and if the peer successfully sends us the piece the status will transition to {@link Status#COMPLETED}</td></tr>
 * <tr><td>{@link Status#COMPLETED}</td><td>Peer of which this Block was downloaded from {@link #getPeer()}</td><td>This block is completed. However, if the hash check fails for the entire {@link Piece}, the status will transition to {@link Status#MISSING} and the peer might end up on some blacklist.</td></tr>
 * </table>
 *
 *
 * After the complete {@link Piece} is completed, use method {@link #releasePeer()} to
 * release any references to the peer.
 *
 * @author: chris.oudeman
 * @since: Jan 10, 2011
 */
public class Block {

	/**
	 * The status of this block. See {@link Block} for an explanation.
	 * @author: chris.oudeman
	 * @since: Jan 10, 2011
	 */
	public enum Status {
		MISSING, REQUESTED, COMPLETED
	}

	private volatile PeerAddress peer;
	private volatile Status status;

	/**
	 * Constructs a new {@link Block} with {@link Status} set to {@link Status#MISSING}
	 */
	public Block() {

		status = Status.MISSING;
	}

	/**
	 * Returns the peer who contributed this block. If the status is {@link Status#REQUESTED},
	 * this block is in the request queue of this peer until we're either chocked, the client disconnects
	 * or the piece if downloaded successfully. If the status is {@link Status#COMPLETED} this
	 * method will return the peer from who we downloaded this block from. This value is available
	 * until {@link #releasePeer()} is called. Finally, if the status of this block is {@link Status#MISSING}
	 * this method will return <b> null</b>.
	 */
	public PeerAddress getPeer() {

		return peer;
	}

	/**
	 * Updates this {@link Status} to {@link Status#REQUESTED} and updates
	 * the peer value.
	 */
	public void setRequested(PeerAddress requestedFrom) {

		peer = requestedFrom;
		status = Status.REQUESTED;
	}

	/**
	 * Updates this {@link Status} to {@link Status#COMPLETED} and updates
	 * the peer value.
	 */
	public void setCompleted(PeerAddress completedFrom) {

		peer = completedFrom;
		status = Status.COMPLETED;
	}

	/**
	 * Updates the {@link Status} to {@link Status#MISSING} and sets this
	 * {@link #getPeer()} value to <b>NULL</b>.
	 */
	public void setMissing() {

		peer = null;
		status = Status.MISSING;
	}

	/**
	 * Returns the {@link Status} of this block.
	 */
	public Status getStatus() {

		return status;
	}

	/**
	 * Sets the peer value to <b>null</b> to release any references.<br/>
	 * Subsequent calls to {@link #getPeer()} REGARDLESS of {@link Status} will
	 * return <b>null</b>.
	 *
	 */
	public void releasePeer() {

		peer = null;
	}

	/**
	 * Returns true is {@link #getStatus()} == {@link Status#COMPLETED}
	 */
	public boolean isCompleted() {

		return getStatus() == Status.COMPLETED;
	}

}


